package com.zhijie.common.util;

public class SnowflakeIdUtils {
    private static SnowflakeIdUtils snowflakeIdUtils;

    static{
        snowflakeIdUtils = new SnowflakeIdUtils();
    }
    /**
     * 起始的时间戳
     */
    private final static long twepoch = 1548950400000L;

    /**
     * 每一部分占用的位数
     */
    private final static long sequenceBits = 12L;

    /**
     * 每一部分的最大值
     */
    private final static long maxSequence = -1L ^ (-1L << sequenceBits);

    /**
     * 每一部分向左的位移
     */
    private final static long timestampShift = sequenceBits;

    private long sequence = 0L; // 序列号
    private long lastTimestamp = -1L; // 上一次时间戳

    private SnowflakeIdUtils() {

    }

    private synchronized long getNextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & maxSequence;
            if (sequence == 0L) {
                timestamp = tilNextMillis();
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;

        return (timestamp - twepoch) << timestampShift // 时间戳部分
                | sequence; // 序列号部分
    }

    private long tilNextMillis() {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 直接调用获取主键
     * @return
     */
    public static long nextId(){
        return snowflakeIdUtils.getNextId();
    }
//    public static void main(String[] args) throws InterruptedException {
//        long start = System.currentTimeMillis();
//        for(int i = 0;i<10;i++){
//            System.out.println(SnowflakeIdUtils.nextId());
//        }
//        long end = System.currentTimeMillis();
//        System.out.println((end-start));
//    }
}
